import datetime
import json
import threading
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from threading import Thread
from time import sleep
from typing import Optional, Callable

from src.core.zhxy_client import ZhxyClient


class ActivityGrabber(Thread):
    """单活动多线程报名器"""

    API = 'https://zhxygateway.gdip.edu.cn/api/student/activity/addActivityEnrollApply'
    MENU_ADDRESS = "app"

    def __init__(self, client: ZhxyClient, activity_id: str, thread_count: int = 10, output: Callable = print,
                 stop_callback: Callable = None):
        """
        初始化报名器
        Args:
            client: 客户端实例
            activity_id: 活动ID
            thread_count: 线程数量，默认10个
            output: 输出函数
            stop_callback: 停止时的回调函数
        """
        super().__init__()
        self.client = client
        self.activity_id = activity_id
        self.thread_count = thread_count
        self.is_running = False
        self.executor: Optional[ThreadPoolExecutor] = None
        self.stop_callback: Optional[Callable] = stop_callback
        self._output: Optional[Callable] = output
        self._lock = threading.Lock()  # 添加线程锁
        self._stop_event = threading.Event()  # 添加停止事件
        self.output_flag = True

    def run(self):
        """启动抢活动"""
        if self.is_running:
            self.safe_output("已经在执行了！", 'warning')
            return

        self.is_running = True
        self._stop_event.clear()  # 清除停止事件
        self.executor = ThreadPoolExecutor(max_workers=self.thread_count)

        self.safe_output("############ 开始执行 ############", 'info')
        self.safe_output(f"启动{self.thread_count}个线程", 'info')
        # 提交所有任务
        futures = []
        payload = self._generate_payload()
        for i in range(self.thread_count):
            future = self.executor.submit(self._grab_task, payload, i)
            futures.append(future)

        # 等待第一个成功的结果
        try:
            for future in as_completed(futures):
                result = future.result()
                if result:
                    break
        except Exception as e:
            self.safe_output(f"抢活动过程发生异常: {e}", "error")
        finally:
            # 只有当任务仍在运行时才调用stop
            if self.is_running:
                self.stop()

    def stop(self):
        """停止抢活动"""
        with self._lock:
            if not self.is_running:
                return
            self.is_running = False
            self.output_flag = False
            self._stop_event.set()  # 设置停止事件

            # 保存引用
            executor = self.executor
            callback = self.stop_callback
            self.executor = None

        # 强制关闭线程池中的所有线程
        if executor:
            # 首先尝试正常关闭，给线程一个短暂的机会完成
            executor.shutdown(wait=False)

        # 在锁外执行回调
        ActivityGrabber._func_exec(callback)

        # 等待0.1秒输出停止信息，确保输出顺序
        def print_stop():
            sleep(0.1)
            self.safe_output("############ 停止执行 ############", 'warning', output_force=True)
        Thread(target=print_stop).start()

    def _generate_payload(self) -> str:
        """生成payload"""
        payload = {
            "relActivityWid": self.activity_id,
            "studentTeacherId": self.client.get_user_id(),
        }
        return json.dumps(payload)

    def _grab_task(self, payload: str, thread_id: int) -> bool:
        """
        单个线程的抢活动任务
        Args:
            payload: 抢活动请求体
            thread_id: 线程id
        Returns:
            是否抢到活动
        """
        attempt = 0
        flag = False
        while not self._stop_event.is_set():  # 使用事件检查而不是直接检查标志
            attempt += 1
            current_time = datetime.datetime.now().strftime("%Y年%m月%d日 %H时%M分%S秒")
            tag = f"{current_time} 线程{thread_id} 第{attempt}次尝试："
            try:
                # 发送报名活动请求
                flag, res = self.client.post(url=ActivityGrabber.API, payload=payload,
                                          menu_address=ActivityGrabber.MENU_ADDRESS)
                if not flag:
                    self.safe_output(tag + res, "error")
                else:
                    res = json.loads(res)
                    if res['code'] == '0' and res['message'] == 'success':
                        self.safe_output(tag + "抢到活动！", 'success')
                        flag = True
                        self._stop_event.set()  # 通知其他线程停止
                        break                   # 抢到活动后退出循环
                    elif res['message'] == '报名失败:已有报名申请记录':
                        self.safe_output(tag + "已有报名申请记录！", "warning")
                        flag = True
                        self._stop_event.set()  # 通知其他线程停止
                        break                   # 已有记录后退出循环
                    else:
                        self.safe_output(tag + res['message'], 'error')

                # 短暂延迟，避免过于频繁的请求
                time.sleep(0.1)

            except Exception as e:
                self.safe_output(tag + str(e), "error")
                time.sleep(0.5)  # 出错时等待稍长时间

        return flag

    def safe_output(self, *args, output_force: bool = False):
        """
        安全输出函数
        Args:
            *args: 输出内容
            output_force: 是否强制输出
        """
        if self.output_flag or output_force:
            ActivityGrabber._func_exec(self._output, *args)

    @staticmethod
    def _func_exec(func: Callable, *args):
        """函数执行器，用于执行通过参数传递进来的函数"""
        if func and callable(func):
            return func(*args)
        return None